﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Snake
{
    class Game
    {
        private Random rnd;
        private Drawer drawer;
        private List<Node> nodes;
        private Node targetNode;
        private KeyboardHendler kh;
        private int winCounts;
        private int snakeSpeed;
        private Direction Direction { get; set; }

        public void StartGame(int borderWidth, int borderHeight, int startX, int startY, char borderSymbol, ConsoleColor borderColor, 
                              char nodeSymbol, ConsoleColor nodeColor, ConsoleColor headColor, char targetNodeSymbol, ConsoleColor targetNodeColor)
        {
            Console.CursorVisible = false;
            snakeSpeed = 400;
            rnd = new Random();
            drawer = new Drawer();
            nodes = new List<Node>();
            kh = new KeyboardHendler();
            kh.KeyRight += MoveRight;
            kh.KeyLeft += MoveLeft;
            kh.KeyUp += MoveUp;
            kh.KeyDown += MoveDown;
            kh.KeySpace += Pause;

            nodes.Add(new Node((borderWidth - 1)/2, borderHeight - 2, headColor, nodeSymbol));
            targetNode = GenerateRandomTargetNode(borderWidth, borderHeight, startX, startY, targetNodeSymbol, targetNodeColor);
            drawer.DrawNode(targetNode);

            bool isGameOver = false;
            Direction = Direction.Up;
            do
            {
                Console.Clear();
                bool isNewRound = false;
                do
                {
                    Console.SetCursorPosition(0, 0);
                    drawer.DrawBorder(borderWidth, borderHeight, startX, startY, borderSymbol, borderColor);
                    drawer.DrawNodeList(nodes);
                    drawer.DrawNode(targetNode);
                    drawer.DrawScore(5, winCounts);
                    MoveSnakeUnit(nodes);
                    Thread.Sleep(snakeSpeed);
                    kh.QueryKB();

                    if (Direction == Direction.Right)
                    {
                        MoveRight();
                    }
                    if (Direction == Direction.Left)
                    {
                        MoveLeft();
                    }
                    if (Direction == Direction.Up)
                    {
                        MoveUp();
                    }
                    if (Direction == Direction.Down)
                    {
                        MoveDown();
                    }

                    if (IsHeadOnBorder(nodes[0].X, nodes[0].Y, drawer) || IsHeadOnBody(nodes[0].X, nodes[0].Y, nodes))
                    {
                        isGameOver = true;
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.SetCursorPosition(startX + borderWidth + 5, startY + 2);
                        Console.Write("Game Over");
                        Console.ReadKey();
                        break;
                    }

                    if (IsHeadOnTarget(nodes[0].X, nodes[0].Y) == true)
                    {
                        winCounts++;
                        if (winCounts > 0 && winCounts <= 20)
                        {
                            snakeSpeed -= 10;
                        }
                        nodes.Add(new Node(nodes[nodes.Count-1].X, nodes[nodes.Count-1].Y, nodeColor, nodeSymbol));
                        drawer.ClearNode(targetNode);
                        isNewRound = true;
                    }
                } while (!isNewRound);

                do
                {
                    targetNode = GenerateRandomTargetNode(borderWidth, borderHeight, startX, startY, targetNodeSymbol, targetNodeColor);
                } while (IsTargetOnSnake(targetNode.X, targetNode.Y, nodes));
                drawer.DrawNode(targetNode);

            } while (!isGameOver);
        }

        public void MoveSnakeUnit(List<Node> nodes)
        {
            for (int i = nodes.Count - 1; i >= 1; i--)
            {
                nodes[i].X = nodes[i - 1].X;
                nodes[i].Y = nodes[i - 1].Y;
            }
        }

        private Node GenerateRandomTargetNode(int borderWidth, int borderHeight, int startX, int startY, char targetNodeSymbol, ConsoleColor targetNodeColor)
        {
            return new Node(rnd.Next(startX + 1, startX + borderWidth - 2), rnd.Next(startY + 1, startY + borderHeight - 2), targetNodeColor, targetNodeSymbol);
        }

        public bool IsHeadOnTarget(int x, int y)
        {
            if (x == targetNode.X && y == targetNode.Y)
                return true;
            return false;
        }

        public bool IsHeadOnBody(int x, int y, List<Node> nodes)
        {
            for (int i = 1; i < nodes.Count; i++)
            {
                if (x == nodes[i].X && y == nodes[i].Y)
                    return true;
            }
            return false;
        }

        public bool IsHeadOnBorder(int x, int y, Drawer drawer)
        {
            if ((x == drawer.X + drawer.Width - 1) || (x == drawer.X) || (y == drawer.Y + drawer.Height - 1) || (y == drawer.Y))
                return true;
            return false;
        }

        public bool IsTargetOnSnake(int x, int y, List<Node> nodes)
        {
            foreach (var node in nodes)
                if (x == node.X && y == node.Y)
                    return true;
            return false;
        }

        public void MoveRight(object sender, EventArgs e)
        {
            if (Direction != Direction.Left)
                Direction = Direction.Right;
        }

        public void MoveLeft(object sender, EventArgs e)
        {
            if (Direction != Direction.Right)
                Direction = Direction.Left;
        }

        public void MoveUp(object sender, EventArgs e)
        {
            if (Direction != Direction.Down)
                Direction = Direction.Up;
        }

        public void MoveDown(object sender, EventArgs e)
        {
            if (Direction != Direction.Up)
                Direction = Direction.Down;
        }

        public void Pause(object sender, EventArgs e)
        {
            ConsoleKeyInfo key;
            do
            {
                key = Console.ReadKey(true);
            } while (key.Key != ConsoleKey.Spacebar);
        }

        public void MoveRight()
        {
            if (IsHeadOnBorder(nodes[0].X, nodes[0].Y, drawer) == false)
            {
                nodes[0].MoveRight();
            }
        }

        public void MoveLeft()
        {
            if (IsHeadOnBorder(nodes[0].X, nodes[0].Y, drawer) == false)
            {
                nodes[0].MoveLeft();
            }
        }

        public void MoveUp()
        {
            if (IsHeadOnBorder(nodes[0].X, nodes[0].Y, drawer) == false)
            {
                nodes[0].MoveUp();
            }
        }

        public void MoveDown()
        {
            if (IsHeadOnBorder(nodes[0].X, nodes[0].Y, drawer) == false)
            {
                nodes[0].MoveDown();
            }
        }
    }

    enum Direction
    {
        Right,
        Left,
        Up,
        Down
    }
}
